package fun.easycode.webterminal.service.impl;

import fun.easycode.webterminal.common.contract.DynamicOperate;
import fun.easycode.webterminal.common.contract.PageDTO;
import fun.easycode.webterminal.common.contract.R;
import fun.easycode.webterminal.common.util.JacksonUtil;
import fun.easycode.webterminal.dao.Machine;
import fun.easycode.webterminal.dao.MachineRepository;
import fun.easycode.webterminal.dao.ScriptTemplate;
import fun.easycode.webterminal.dao.ScriptTemplateRepository;
import fun.easycode.webterminal.service.ScriptTemplateService;
import fun.easycode.webterminal.service.cmd.ScriptTemplateExecCmd;
import fun.easycode.webterminal.service.cmd.ScriptTemplatePageQry;
import fun.easycode.webterminal.service.cmd.ScriptTemplateSaveCmd;
import fun.easycode.webterminal.service.cmd.ScriptTemplateSaveCmdAssembler;
import fun.easycode.webterminal.service.dto.ScriptResultDTO;
import fun.easycode.webterminal.service.dto.ScriptTemplateDTO;
import fun.easycode.webterminal.service.dto.ScriptTemplateDTOAssembler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;

@Slf4j
@Service
public class ScriptTemplateServiceImpl implements ScriptTemplateService {

    @Resource
    private ScriptTemplateRepository scriptTemplateRepository;
    @Resource
    private MachineRepository machineRepository;

    @Override
    public R<String> delete(String id) {
        boolean r = scriptTemplateRepository.removeById(id);

        return r? R.message("删除成功") : R.error("删除失败");
    }

    @Override
    public R<ScriptTemplateDTO> getById(String id) {

        ScriptTemplate template = scriptTemplateRepository.getById(id);

        if(template == null){
            return R.error("未找到该模板");
        }

        return R.success(ScriptTemplateDTOAssembler.INSTANCE.to(template));
    }

    @Override
    public R<PageDTO<ScriptTemplateDTO>> page(ScriptTemplatePageQry cmd) {

        cmd.validate();

        return R.success(DynamicOperate.page(cmd
                , scriptTemplateRepository.getBaseMapper()
                , ScriptTemplateDTOAssembler.INSTANCE::to));
    }

    @Override
    public R<String> save(ScriptTemplateSaveCmd cmd) {
        cmd.validate();

        boolean r = scriptTemplateRepository.saveOrUpdate(ScriptTemplateSaveCmdAssembler.INSTANCE.to(cmd));

        return r ? R.message("保存成功!") : R.error("保存失败!");
    }

    @Override
    @Async
    public void exec(ScriptTemplateExecCmd cmd, SseEmitter emitter) {

        cmd.validate();

        ScriptTemplate template = scriptTemplateRepository.getById(cmd.getId());

        // 发布器, 函数式接口，主要是为了确认js一定是发布的ScriptResultDTO类对象
        Consumer<ScriptResultDTO> publisher = (result) -> {
            SseEmitter.SseEventBuilder event = SseEmitter.event()
                    .data(JacksonUtil.toJson(result))
                    .id(cmd.getId())
                    .name("message");
            try {
                emitter.send(event);
            } catch (IOException e) {
                emitter.completeWithError(e);
            }
        };;


        if(template == null){
            publisher.accept(new ScriptResultDTO(false, "未找到该模板"));
            return;
        }

        List<Machine> machines = machineRepository.listByIds(cmd.getMachineIds());

        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.put("publisher", publisher);

        String message = "开始执行脚本: " + template.getName() + "。";
        publisher.accept(new ScriptResultDTO(true, message));

        for(Machine machine : machines){
            engine.put("machine", machine);
            try {
                engine.eval(template.getScript());
            } catch (Exception e) {
                log.info("exec script error", e);
                publisher.accept(new ScriptResultDTO(false, e.getMessage()));
            }
        }
        message = "脚本执行完毕: " + template.getName() + "。";
        publisher.accept(new ScriptResultDTO(true, message));
        emitter.complete();
    }

}
